31/05/2010

Point théorique Java : Assert

- l'activation des asserts se fait au moment du lancement d'un programme en utilisant le paramètre -ea (pour enable assertion)

- Lorsque les assertions sont désactivées, les expressions passées aux asserts ne sont pas évaluées


...
int i = 0;
assert (i=4)==4;
// ici i vaut 4 si les assertions sont activées et 0 sinon
...


- Attention : ceci est un cas particulier très étrange :
Lorsque l'on utilise un objet avant d'avoir initialisé sa classe, il doit se comporter comme si les assertions étaient actives.

class P{
static {Q.method();}
}
class Q extends P{
static void method(){
int i = 0;
assert (i=4)==4;
System.out.println(i);
}
}

Ici l'initialisation de Q entraine celle de P qui appelle Q.method() qui est donc exécutée avant que Q soit initialisée.
Que les assertions soient activées ou non, le println affichera 4;

28/05/2010

Point théorique Java : Boxing Unboxing

1) Principe
A chaque type de primitive numérique ou booleéne est associée un Wrapper.
Les Wrappers sont : Boolean, Byte, Short, Character, Integer, Long, Float et Double.
Tous à l'exception de Boolean dérivent de Number.

Lors d'une assignation, d'un passage de paramètre à une fonction, d'une opération ou d'un cast explicite, on peut utiliser une valeur primitive à la place de son Wrapper et réciproquement.
La conversion qui intervient à ce moment là est appellée boxing (primitive -> wrapper) ou unboxing (wrapper->primitive)
Exemples :
Integer I1 = 4;
Vector MonVector = new Vector(); MonVector.add(2.0);
int i2 = I1 +7;
if (i2 > I1) ;
Byte b = (byte)i2;

2) Ce que le boxing/unboxing ne fait pas
Le Boxing / Unboxing peut convertir une primitive en son wrapper associé et réciproquement.
Le Boxing ne peut pas convertir une primitive en un wrapper associé à une autre primitive.
Long l = 2; // ceci n'est pas légal
Le Unboxing ne peut pas convertir un wrapper en une primitive associée à un autre wrapper.
byte b = (Integer)2; // ceci n'est pas légal
Le Boxing/Unboxing ne se fait que quand on passe explicitement un Wrapper (respectivement une primitive), là où une primitive (resp un Wrapper) est attendu.
Double f = 2.0, g= 2.0; // ici (f == g) renverait false car les objets sont comparés directement sans être unboxés
Float h = 2.0f; // ici (f == h) provoquerait une erreur de compilation

3) Mutualisation
Au lançement de la jvm, celle-ci crée un certain nombre de Wrappers par défaut.
Ces wrappers sont automatiquement utilisés lors du boxing des valeurs auxquelles ils correspondent.
Ainsi, lorsque l'on écrit :
Byte b1 = 1;
Byte b2 = 1;
On a b1 == b2. C'est à dire que les deux références b1 et b2 désignent le même objet.
Les Wrappers par défaut sont :
- Tous les Byte
- Les Character compris entre 0 et 127
- Les Short, Integer et Long compris entre -128 et 127

Point théorique Java : Conversions de primitives numériques


1) Règle générale

Les conversions élargissantes sont autorisées, qu'elles soient implicites ou explicites :
byte -> short -> int -> long -> float -> double
char -> int /

ex: int i = 4;
long l = i;
float f = (float)i;

Les conversions rétrécissantes explicites sont autorisées :
ex: int i = 4;
byte b = (byte)i;

contrex : char c = i; // illégal

2) Elargissant ne signifie pas qu'il n'y a pas de perte de précision
Il faut remarquer que si la conversion de toute valeur entière en valeur à virgule flottante est considérée comme une conversion élargissante, cela ne signifie pas pour autant qu'elle se fait sans perte de précision.
ex : System.out.println( ((int)(float)1234567890) - 1234567890); // ceci affiche 46

3) Cast de valeur flottante en valeur entière
Tout cast de double ou float en byte, short, ou char se fait en 2 étapes :
- cast de double ou float en int
- cast de int en byte short ou char

Ceci peut induire des effets de bord :
double D = 0x80000000p0;
long L = 0x80000000L;
System.out.println((short)D); // cette ligne affiche -1, car (int)D vaut 0x7FFFFFFF
System.out.println((short)L); // cette ligne affiche 0, car (int)L vaut -0x80000000

Rappel : Le cast d'un float ou un double supérieur à la plus grande valeur que peut contenir un int (0x7FFFFFFF) donne cette valeur.

4) Exceptions

Les conversion rétrécissante implicite sont autorisées quand les conditions suivantes sont remplies simultanément:
- il s'agit d'une assignation à une variable de type byte, short ou char ;
- les valeurs assignées sont des valeurs littérales ou finales ou des opérations de valeurs littérales ou finales;
- les valeurs assignées sont dans la fourchette d'existance de la variable à assigner (cela implique entre autre qu'elles sont entières et non à virgule)

Il s'agit d'un cas particulier fait pour faciliter le codage :
ex : byte b1 = 2; // ceci devrait être illégal car "2" est un entier, mais c'est légal
ex : short s = 2+2; // même chose
contrex : byte b2 = 128; // ceci est illégal car un byte ne peut contenir le nombre 128
contrex : char c = 5-6; // ceci est illégal car un byte ne peut contenir le nombre -1